package com.yehee.chainserver.common;

import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.yehee.chainserver.common.entity.*;
import com.yehee.chainserver.mapper.ChainDepositMapper;
import com.yehee.chainserver.mapper.EthWalletMapper;
import com.yehee.chainserver.mapper.WalletConfigMapper;
import com.yehee.chainserver.util.SpringContextUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 服务的父类,所有服务继承父类
 */
@Log4j2


public abstract class BaseChainServer {

    /**
     * 当前服务运行状态
     */
    public boolean running = false;
    /**
     * 未确认的充值
     */
    public Map<String, Integer> unConfirm = new HashMap<>();
    /**
     * 扫描这些地址
     */
    public Set<String> address = new HashSet<>(400);

    public static final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    protected ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public void start() {
        cachedThreadPool.submit(() -> {
            try {
                running = true;
                run();
            } catch (Throwable e) {
                running = false;
                log.error(e.getMessage(), e);
            }
        });
    }

    public static final AtomicLong count = new AtomicLong(0);

    /**
     * 当前服务名字
     */
    public abstract String getModuleName();

    /**
     * 当前服务运行状态
     */
    public boolean getStatus() {
        return running;
    }

    /**
     * 服务运行执行的方法
     */
    protected abstract void run() throws Throwable;

    /**
     * 新账户
     *
     * @param coinCode 币种,可能是代币
     * @return
     */
    public abstract String newAccount(String coinCode) throws Throwable;

    /**
     * 检测地址是否正确
     */
    public abstract boolean checkAddress(String address);

    /**
     * 提币
     *
     * @param coinCode 币种,可能是代币
     * @param address  提币地址
     * @param number   提币数量
     * @param memo     备注 (可以不使用)
     * @return
     */
    public abstract ChainWithdrawResponse withdraw(String coinCode, String address, BigDecimal number, String memo) throws Throwable;

    /**
     * 确认数
     */
    public abstract void confirm() throws Throwable;

    /**
     * 推送充值
     *
     * @param deposit
     * @return
     */
    public String deposit(ChainDepositResponse deposit) {
        log.info(" =====================  充值日志  ======================");
        log.info(" 币种: " + deposit.getCoinCode());
        log.info(" 数量: " + deposit.getAmount());
        log.info(" memo: " + deposit.getMemo());
        log.info(" hash: " + deposit.getHash());
        log.info(" ...... ");

        ChainDepositMapper chainDepositMapper=SpringContextUtils.applicationContext.getBean(ChainDepositMapper.class);
        chainDepositMapper.insert(deposit);
        return deposit.getHash();

    }

    /**
     * 区块确认数量 +1  订单
     *
     * @return
     */
    protected Integer confirmNumber(Integer currentConfirmNumber, String orderId) {

        log.info("{} 订单确认数+1",orderId);
        return 1;
    }

    /**
     * 完成充值
     *
     * @return
     */
    protected abstract boolean completeDeposit(String orderId,Integer userId,BigDecimal amout);


    /**
     * 获得需要监控的地址
     *
     * @return
     */
    public abstract List<String> getAddress() ;

    public abstract ChainBalance getChainBalance(String coinCode, String feeCoinCode) throws Throwable;


    protected static final Map<String, ChainCoin> CHAIN_COIN_CACHE = new ConcurrentHashMap<>();


    @Autowired
    WalletConfigMapper walletConfigMapper;

    protected WalletConfig getCoinCode(String coinCode) {
        return walletConfigMapper.selectBySymbol(getModuleName());
    }

    @Cached(expire = 10, timeUnit = TimeUnit.MINUTES, cacheType = CacheType.LOCAL)
    protected int getConfirmNumber(String coinCode) {

        return getCoinCode(coinCode).getConfirmSum();
    }

    @Cached(expire = 10, timeUnit = TimeUnit.MINUTES, cacheType = CacheType.LOCAL)
    protected BigDecimal getDepositMin(String coinCode) {
        return BigDecimal.valueOf(0);
    }

    protected ChainDepositResponse createDepositEntity(String hash, BigDecimal number, String conCode, String module,
                                                       String status, String toAddress, String memo,Integer userId) {
        ChainDepositResponse deposit = new ChainDepositResponse();
        deposit.setHash(hash);
        deposit.setAmount(number);
        deposit.setTimestamp(System.currentTimeMillis());
        deposit.setModule(module);
        deposit.setCoinCode(conCode);
        deposit.setStatus(status);
        deposit.setToAddress(toAddress);
        deposit.setMemo(memo);
        deposit.setUserId(userId);
        return deposit;
    }

}
